﻿using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Threading;
using System;
using xuni;

namespace xuni
{


    /// <summary>
    /// 用于异步线程和主线程交互(通信)
    /// </summary>
    public static class MainThreadSync
    {

        /// <summary>
        /// 交互消息
        /// </summary>
        class SyncMsg
        {
            internal Action<UserData> func;

            internal UserData userData = new UserData(); 
 
            internal void Reset()
            {
                func = null;

                userData.i = 0;
                userData.s = "";
                userData.obj = null;
            }
        }


        static Pool<SyncMsg> s_criticalMsgPool; //主线程和异步线程都会从这个pool获取消息

        static List<SyncMsg> s_criticalMsgList; //异步线程在这边添加消息, 主线程在这边获取消息
        static List<SyncMsg> s_mainThreadMsgList;

        static int s_mainThreadId;

        internal static void Initialize()
        {
            s_criticalMsgPool = new Pool<SyncMsg>((sender) => new SyncMsg(), (sender, msg) => { msg.Reset(); });

            s_criticalMsgList = new List<SyncMsg>();
            s_mainThreadMsgList = new List<SyncMsg>();

            s_mainThreadId = Thread.CurrentThread.ManagedThreadId;
        }

        internal static void OnUpdate()
        {
            if (s_criticalMsgList.Count > 0)
            {
                if (Monitor.TryEnter(s_criticalMsgList))
                {
                    try
                    {
                        if (s_criticalMsgList.Count > 0)
                        {
                            s_mainThreadMsgList.AddRange(s_criticalMsgList);
                            s_criticalMsgList.Clear();
                        }
                    }
                    finally
                    {
                        Monitor.Exit(s_criticalMsgList);
                    }
                }
            }

            var ct = s_mainThreadMsgList.Count;
            if (ct > 0)
            {
                int removeCt = 0;
                for (var i = 0; i < ct; ++i)
                {
                    var msg = s_mainThreadMsgList[i];
                    if (null == msg)
                        continue;

                    SafeInvokeDelegate(msg);

                    RecycleMsg(msg);
                    s_mainThreadMsgList[i] = null;
                    removeCt++;
                }

                //清理List
                if (removeCt > 0)
                {
                    for (var i = ct - 1; i >= 0; --i)
                    {
                        var msg = s_mainThreadMsgList[i];
                        if (null == msg)
                            s_mainThreadMsgList.RemoveAt(i);
                    }
                }
            }
        }

        static void RecycleMsg(SyncMsg msg)
        {
            if (Monitor.TryEnter(s_criticalMsgPool)) //失败就不回收
            {
                try
                {
                    s_criticalMsgPool.Recycle(msg);
                }
                finally
                {
                    Monitor.Exit(s_criticalMsgPool);
                }
            }
        }

        static void SafeInvokeDelegate(SyncMsg msg)
        {
            /*
            if (null == handler)
                return;
                */

            try
            {
                msg.func.Invoke(msg.userData);
            }
            catch (Exception ex)
            {
                Debug.Log($"invoke callback fail: msg: {ex.Message}, st: {ex.StackTrace}");
            }
        }

        public static void QueueOnMainThread(Action<UserData> func)
        {
            SyncMsg msg;
            lock (s_criticalMsgPool)
                msg = s_criticalMsgPool.Obtain();
            msg.func = func;

            lock (s_criticalMsgList)
                s_criticalMsgList.Add(msg);
        }

        public static void QueueOnMainThread(Action<UserData> func, int i = 0, string s = "", object obj = null)
        {
            SyncMsg msg;
            lock (s_criticalMsgPool)
            {
                msg = s_criticalMsgPool.Obtain();
            }
            msg.func = func;
            var udata = msg.userData;
            udata.i = i;
            udata.s = s;
            udata.obj = obj;

            /*
            if (s_mainThreadId == Thread.CurrentThread.ManagedThreadId)
            {
                SafeInvokeDelegate(msg);
                return;
            }
            */

            lock (s_criticalMsgList)
            {
                s_criticalMsgList.Add(msg);
            }
        }

    } //end of class




    /*
    public static class MainThreadSync
    {
        class SyncMsg
        {
            public long v;

            public void Reset()
            {
                v = 0;
            }
        }

        static Dictionary<long, Action<long>> s_broadcastListeners;

        static Pool<SyncMsg> s_mainThreadMsgPool;
        static Pool<SyncMsg> s_criticalMsgPool;

        static List<SyncMsg> s_criticalMsgList;
        static List<SyncMsg> s_mainThreadMsgList;

        static int _threadId;

        //++++++++++ broadcast

        public static void On(long evtId, Action<long> callback)
        {
            if (null == s_broadcastListeners)
                s_broadcastListeners = new Dictionary<long, Action<long>>();

            if (s_broadcastListeners.TryGetValue(evtId, out var c) && null != c)
            {
                Debug.Log($"listener exist for evt: {evtId}");
                return;
            }

            s_broadcastListeners[evtId] = callback;
        }

        public static void Off(long evtId, Action<long> callback)
        {
            if (null != s_broadcastListeners)
                s_broadcastListeners.Remove(evtId);
        }

        //++++++++++
        

        internal static void Init()
        {
            s_mainThreadMsgPool = new Pool<SyncMsg>((sender) => new SyncMsg(), (sender, msg) => { msg.Reset(); });
            s_criticalMsgPool = new Pool<SyncMsg>((sender) => new SyncMsg(), (sender, msg) => { msg.Reset(); });

            s_criticalMsgList = new List<SyncMsg>();
            s_mainThreadMsgList = new List<SyncMsg>();
        }

        internal static void Update()
        {
            if (s_criticalMsgList.Count > 0)
            {
                if (Monitor.TryEnter(s_criticalMsgList))
                {
                    try
                    {
                        if (s_criticalMsgList.Count > 0)
                        {
                            s_mainThreadMsgList.AddRange(s_criticalMsgList);
                            s_criticalMsgList.Clear();
                        }
                    }
                    finally
                    {
                        Monitor.Exit(s_criticalMsgList);
                    }
                }
            }

            var ct = s_mainThreadMsgList.Count;
            if (ct > 0)
            {
                int removeCt = 0;
                for (var i = 0; i < ct; ++i)
                {
                    var msg = s_mainThreadMsgList[i];
                    if (null == msg)
                        continue;

                    var evtId = msg.v;
                    if (s_broadcastListeners.TryGetValue(evtId, out var cb))
                    {
                        try
                        {
                            cb.Invoke(evtId);
                        }
                        catch (Exception ex)
                        {
                            Debug.Log($"invoke callback fail: msg: {ex.Message}");
                        }
                    }

                    s_mainThreadMsgList[i] = null;
                    removeCt++;
                }

                if (removeCt > 0)
                {
                    for (var i = ct - 1; i >= 0; --i)
                    {
                        var msg = s_mainThreadMsgList[i];
                        if (null == msg)
                            s_mainThreadMsgList.RemoveAt(i);
                    }
                }
            }
        }

        public static void QueueOnMainThread(long v)
        {
            SyncMsg msg;
            lock (s_criticalMsgPool)
            {
                msg = s_criticalMsgPool.Obtain();
            }
            msg.v = v;

            lock (s_criticalMsgList)
            {
                s_criticalMsgList.Add(msg);
            }
        }

    } //end of class
    */

}
